/*****************************************************************************/
/*                        >>> func_disco.c-- <<<                             */
/*	Trabajo Practico 2do. cuatrimestre 2011                                  */
/*	TP:"FUSELAJE"                                                            */
/*	GRUPO: Reduce Fat Fast                                                   */
/*	INTEGRANTES: Emanuel Genovese                                            */
/*               Federico Gabriel Garcia                                     */
/*               Federico Pioli                                              */
/*               Esther Insfrain                                             */
/*               Luciano Bagattin                                            */
/*	Fecha UM:                                                                */
/*****************************************************************************/
/*****************************************************************************/
/*		        	HEADERS					                                 */
/*****************************************************************************/
#include <stdlib.h>
#include "../headers/func_Disco.h"
#include "../headers/estructuras.h"
#include "../headers/func_PPNIC.h"
#include "../headers/variables_globales.h"
#include "../headers/func_rutinas.h"
#include "../headers/constantes.h"
#include "constants_commons.h"
#include "func_ipc.h"
#include "log.h"
#include "string.h"
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

/*****************************************************************************/
/*		        	DEFINE					                                 */
/*****************************************************************************/

/******************************************************************************/
/* Nombre de Funcion: fProcesarAtencionPedidos                                */
/* Parametros: stM:un puntero a void                                          */
/* Descripcion: Funcion que se encarga de seleccionar el sector a atender     */
/******************************************************************************/
void * fProcesarAtencionPedidos(void * stM) {
	stManejoHilo *stMH = (stManejoHilo *) stM;
	stNIPC stEnvio;
	int *iResultado = 0;
	stTrace stT;
	stRLecturaSector stP;
	float fTiempo;
	stNodoPedido *stAux = (stNodoPedido *) malloc(sizeof(stNodoPedido));
	stREscrituraSector stRPES;
	void *sPayload2;
	char sCadena[1024] = "\0";
	char sCadenaFin[1024] = "\0";
	char sCadena1[1024] = "\0";
	int iPista;
	stAux = NULL;

	if (strcmp(stMH->dC->sAlgo_Planif, "S") == 0) {
		while (1) {

			sem_wait(&semColasPedidos1);

			pthread_mutex_lock(&tCola);
			do {
				stAux = fSacarPedidoEnCola(lPista, 1);
			} while (stAux == NULL);
			iPista = (int) (stAux->iNumSector / iSectores);

			pthread_mutex_unlock(&tCola);

			memset(sCadena, '\0', 1024);
			memset(sCadena1, '\0', 1024);
			memset(sCadenaFin, '\0', 1024);

			switch (stAux->iTipoOperacion) {
			case LECTURA:
				stEnvio.iPayloadDescriptor = LECTURA;

				strcpy(stEnvio.sDescriptorOriginal, stAux->sDescriptorOriginal);
				/*Procedemos a buscar en la cache si esta. En caso de que este lo sacamos de ahi y si no lo leeremos*/

				sprintf(sCadena1, "%s%d", "en el Pedido de lectura sector:",
						stAux->iNumSector);
				if (fBuscarEnCache(iPista, stAux->iNumSector, stAux->info, 1)) {
					stP.iRespuesta = OPERACION_OK;

					sprintf(sCadena, "Exito ");
					strcat(sCadena, sCadena1);
					log_info(stMH->log, SNOMBREPROCESO, "Message Info: %s",
							sCadena);
					memset(sCadena, '\0', 1024);
				} else {
					if (fLeerSector(stAux, iPista, stMH->dC->iTiempoLectura)
							== -1) {
						stP.iRespuesta = OPERACION_FAIL;
						sprintf(sCadena, "Error ");
						strcat(sCadena, sCadena1);
						log_info(stMH->log, SNOMBREPROCESO, "Message Info: %s",
								sCadena);
					} else {
						stP.iRespuesta = OPERACION_OK;
						sprintf(sCadena, "Exito ");
						strcat(sCadena, sCadena1);
						log_error(stMH->log, SNOMBREPROCESO,
								"Message Error: %s", sCadena);
					}
					fTiempo = (float) stMH->dC->iTiempoLectura;

					fCargarCache(iPista, stAux->iNumSector, stAux->info);
				} //finn del if
				stP.iSector = (iPista * iSectores) + stAux->iNumSector;

				strcpy(stP.info, stAux->info);
				sPayload2 = (void *) &stP;
				stEnvio.sPayload_lenth = sizeof(stRLecturaSector);
				stEnvio.iType=3;

				break;
			case ESCRITURA:


				fBuscarEnCache(iPista, stAux->iNumSector, stAux->info, 2);
				sprintf(sCadena1, "%s%d", "en el Pedido de escritura sector:",
						stAux->iNumSector);
				if (fEscribirSector(stAux, iPista, stMH->dC->iTiempoEscritura)
						== 0) {
					stRPES.iRespuesta = OPERACION_OK;
					sprintf(sCadena, "Exito ");
					strcat(sCadena, sCadena1);
					log_info(stMH->log, SNOMBREPROCESO, "Message Info: %s",
							sCadena);
					fCargarCache(iPista, stAux->iNumSector, stAux->info);
				} else {
					stRPES.iRespuesta = OPERACION_FAIL;
					sprintf(sCadena, "Error ");
					strcat(sCadena, sCadena1);
					log_error(stMH->log, SNOMBREPROCESO, "Message Error: %s",
							sCadena);
				} //fin del if
				stRPES.iSector = (iPista * iSectores) + stAux->iNumSector;

				stEnvio.iPayloadDescriptor = ESCRITURA;
				stEnvio.iType=2;
				strcpy(stEnvio.sDescriptorOriginal, stAux->sDescriptorOriginal);
				sPayload2 = (void *) &stRPES;
				stEnvio.sPayload_lenth = sizeof(stREscrituraSector);

				break;
			case TRACE:
				fTiempo = (float) stMH->dC->iTiempoLectura;
				stEnvio.iPayloadDescriptor = TRACE;
				strcpy(stEnvio.sDescriptorOriginal, stAux->sDescriptorOriginal);
				stEnvio.iType=4;
				pArmadoTrace(stAux, DISCO, fTiempo, &stT, stMH->dC->iRPM);
				sPayload2 = (void *) &stT;
				stEnvio.sPayload_lenth = sizeof(stT);
				sprintf(sCadena1, "%s%d", "Pedido de Trace sector:",
						stT.iSectorPedido);

				sprintf(sCadena, "Exito ");
				strcat(sCadena, sCadena1);
				log_info(stMH->log, SNOMBREPROCESO, "Message Info: %s",
						sCadena);
				break;
			} //fin del switch
			/*Enviamos el pedido al procedo emisor del pedido*/
			memset(sCadenaFin, '\0', 1024);

			if (fSendPPNIPCXsocket(stAux->iSocket, stEnvio, sPayload2,
					stMH->log) == 0) {
				sprintf(sCadenaFin, "Error en el envio ");
				strcat(sCadenaFin, sCadena1);
				log_error(stMH->log, SNOMBREPROCESO, "Message Error: %s",
						sCadenaFin);

				iResultado = (int*) -1;
				return iResultado;
			} //fin del if
			sprintf(sCadenaFin, "Exito en el envio ");
			strcat(sCadenaFin, sCadena1);
			log_info(stMH->log, SNOMBREPROCESO, "Message Info: %s", sCadenaFin);

		} //fin del while
	} else {
		/*saco los valores de la cola y cuando no tenga mas cambio el valor de */

		iPista = (int) (stAux->iNumSector / iSectores);
		while (1) {
			pthread_mutex_lock(&tCola);
			stAux = fSacarPedidosDeAlgunaCola();
			pthread_mutex_unlock(&tCola);

			switch (stAux->iTipoOperacion) {
			case LECTURA:
				stEnvio.iPayloadDescriptor = LECTURA;
				strcpy(stEnvio.sDescriptorOriginal, stAux->sDescriptorOriginal);
				memset(sCadena, '\0', 1024);
				sprintf(sCadena1, "%s%d", "en el Pedido de lectura sector:",
						stAux->iNumSector);

				if (fBuscarEnCache(iPista, stAux->iNumSector, stAux->info, 1)) {
					/*Lo traemos desde la cache*/
					stP.iRespuesta = OPERACION_OK;
					sprintf(sCadena, "Exito ");
					strcat(sCadena, sCadena1);
					log_info(stMH->log, SNOMBREPROCESO, "Message Info: %s",
							sCadena);
					memset(sCadena, '\0', 1024);
				} else {
					/*Procedemos a leerlo directamente del disco*/
					if (fLeerSector(stAux, iPista, stMH->dC->iTiempoLectura)
							== -1) {
						stP.iRespuesta = OPERACION_FAIL;
						sprintf(sCadena, "Error ");
						strcat(sCadena, sCadena1);
						log_info(stMH->log, SNOMBREPROCESO, "Message Info: %s",
								sCadena);
					} else {
						stP.iRespuesta = OPERACION_OK;
						sprintf(sCadena, "Exito ");
						strcat(sCadena, sCadena1);
						log_error(stMH->log, SNOMBREPROCESO,
								"Message Error: %s", sCadena);
					}
					fTiempo = (float) stMH->dC->iTiempoLectura;

					fCargarCache(iPista, stAux->iNumSector, stAux->info);
				} //finn del if
				stP.iSector = (iPista * iSectores) + stAux->iNumSector;
				strcpy(stP.info, stAux->info);
				sPayload2 = (void *) &stP;
				stEnvio.sPayload_lenth = sizeof(stRLecturaSector);
				stEnvio.iType=3;

				break;
			case ESCRITURA:
				stEnvio.iPayloadDescriptor = ESCRITURA;
				stEnvio.sPayload_lenth = sizeof(stREscrituraSector);
				strcpy(stEnvio.sDescriptorOriginal, stAux->sDescriptorOriginal);
				fBuscarEnCache(iPista, stAux->iNumSector, stAux->info, 2);
				sprintf(sCadena1, "%s%d", "en el Pedido de escritura sector:",
						stAux->iNumSector);
				if (fEscribirSector(stAux, iPista, stMH->dC->iTiempoEscritura)
						== 0) {
					stRPES.iRespuesta = OPERACION_OK;
					sprintf(sCadena, "Exito ");
					strcat(sCadena, sCadena1);
					log_info(stMH->log, SNOMBREPROCESO, "Message Info: %s",
							sCadena);
					fCargarCache(iPista, stAux->iNumSector, stAux->info);
				} else {
					stRPES.iRespuesta = OPERACION_FAIL;
					sprintf(sCadena, "Error ");
					strcat(sCadena, sCadena1);
					log_error(stMH->log, SNOMBREPROCESO, "Message Error: %s",
							sCadena);
				} //fin del if
				stRPES.iSector = (iPista * iSectores) + stAux->iNumSector;
				sPayload2 = (void *) &stRPES;
				stEnvio.iType=2;

				break;
			case TRACE:
				fTiempo = (float) stMH->dC->iTiempoLectura;
				stEnvio.iPayloadDescriptor = TRACE;
				strcpy(stEnvio.sDescriptorOriginal, stAux->sDescriptorOriginal);
				pArmadoTrace(stAux, DISCO, fTiempo, &stT, stMH->dC->iRPM);
				sPayload2 = (void *) &stT;
				stEnvio.sPayload_lenth = sizeof(stT);
				sprintf(sCadena1, "%s%d", "Pedido de Trace sector:",
						stT.iSectorPedido);

				sprintf(sCadena, "Exito ");
				strcat(sCadena, sCadena1);
				log_info(stMH->log, SNOMBREPROCESO, "Message Info: %s",
						sCadena);
				stEnvio.iType=4;
				break;
			} //fin del switch

			/*Enviamos el pedido al procedo emisor del pedido*/
			memset(sCadenaFin, '\0', 1024);

			if (fSendPPNIPCXsocket(stAux->iSocket, stEnvio, sPayload2,
					stMH->log) == 0) {
				sprintf(sCadenaFin, "Error en el envio ");
				strcat(sCadenaFin, sCadena1);
				log_error(stMH->log, SNOMBREPROCESO, "Message Error: %s",
						sCadenaFin);

				iResultado = (int*) -1;
				return iResultado;
			} //fin del if
			sprintf(sCadenaFin, "Exito en el envio ");
			strcat(sCadenaFin, sCadena1);
			log_info(stMH->log, SNOMBREPROCESO, "Message Info: %s", sCadenaFin);
		} //fin del if while
	} //fin del if

	return iResultado;

} //fin del fProcesarAtencionPedidos

/******************************************************************************/
/* Nombre de Funcion: fProcesarPedidos                                        */
/* Parametros: iSocket socket por el cual se procesan los pedidos             */
/* Descripcion: Funcion que hace el procesamiento del ppd como server         */
/******************************************************************************/

int fProcesarPedidos(int iSocket, t_log * log) {
	/*Declaracion de variables locales*/
	stNIPC w, stSIPC;
	char scadena[1024] = "\0";
	int *iSect, iSector;
	void *sPayload = NULL;
	void *sPayload2;
	vecTrace v;

	stPEscrituraSector *stPES = malloc(sizeof(stPEscrituraSector));

	/*Fin de la declaracion de variables locales*/

	if (fRecvPPNIPCXsocket(iSocket, &w, &sPayload, log) <= 0) {
		return -1;
	} //fin del fRecibirIPCXsocket

	memset(scadena, '\0', 1024);
	sprintf(scadena, "%s%d%d%s%d", "Recepcion:", w.sPayload_lenth, w.iType,
			w.sDescriptorOriginal, w.iPayloadDescriptor);
	puts(scadena);
	switch (w.iPayloadDescriptor) {
	case POSICIONCABEZAL:

		stSIPC.iPayloadDescriptor = POSICIONCABEZAL;
		stSIPC.iType = 1;
		strcpy(stSIPC.sDescriptorOriginal, w.sDescriptorOriginal);
		stSIPC.sPayload_lenth = sizeof(vecTrace);
        pPasarLogicoAFisico(iPosicionCabezal,&v);
		sPayload2 = (void *) &v;




		/*Cargo la estructura y la mando a la consola.*/
		if (fSendPPNIPCXsocket(iSocket, stSIPC, sPayload2, log) == 0) {
			log_error(
					log,
					SNOMBREPROCESO,
					"Message error: %s",
					"Error en el send correspondiente a la atencion del cabezal ");
			return -1;
		} //fin del fSendIPCCXsocket
		log_info(log, SNOMBREPROCESO, "Message info: %s",
				"Exito en el send correspondiente a la atencion del cabezal ");
		break;
	case LECTURA: case TRACE:
		iSect = malloc(sizeof(int));
		iSect = (int *) sPayload;
		iSector = *iSect;


		/*Agregamos el pedido en la cola de pedidos*/
		if (!strcmp(stInfoConfig.sAlgo_Planif, "S")) {
			pthread_mutex_lock(&tCola);
			sem_post(&semColasPedidos1);
			fAgregarPedido(iSocket, iSector, w.iPayloadDescriptor, NULL, 1,
					w.sDescriptorOriginal);
			pthread_mutex_unlock(&tCola);
		} else {

			pthread_mutex_lock(&tCola);
			fAgregarPedidoEnAlgunaCola(iSocket, iSector,  w.iPayloadDescriptor, NULL,
					w.sDescriptorOriginal);
			pthread_mutex_unlock(&tCola);

		}
		break;
	case ESCRITURA:

		stPES = (stPEscrituraSector *) sPayload;

		/*Agregamos el pedido en la cola de pedidos*/
		if (!strcmp(stInfoConfig.sAlgo_Planif, "S")) {
			pthread_mutex_lock(&tCola);

			fAgregarPedido(iSocket, stPES->iSector, ESCRITURA, stPES->info, 1,
					w.sDescriptorOriginal);

			pthread_mutex_unlock(&tCola);
			sem_post(&semColasPedidos1);
		} else {

			pthread_mutex_lock(&tCola);
			fAgregarPedidoEnAlgunaCola(iSocket, stPES->iSector, ESCRITURA,
					stPES->info, w.sDescriptorOriginal);
			pthread_mutex_unlock(&tCola);

		}
		break;

	} //fin del switch

	return 0;
} //fin del fProcesarPedidos

/******************************************************************************/
/* Nombre de Funcion: fMapear                                                 */
/* Parametros: sin parametros                                                 */
/* Descripcion: funcion que mapea todo el dispositivo                         */
/******************************************************************************/

int fMapear(t_log *log) {

	if ((descriptor = open(sPath, O_RDWR)) < 0) {
		log_error(log, SNOMBREPROCESO, "Message error: %s",
				"Error en el apertura del archivo");
		return -1;
	} //fin del if
	log_info(log, SNOMBREPROCESO, "Message info: %s", "Se abrio el archivo");
	//Obtener la longitud del archivo a mapear
	fstat(descriptor, &estadobuf);

	//Mapear el archivo de entrada
	if ((map = mmap(NULL, estadobuf.st_size, PROT_READ | PROT_WRITE,MAP_SHARED
			,descriptor,0)) == MAP_FAILED) {
		log_error(log, SNOMBREPROCESO, "Message error: %s",
				"Error en el mapeo");
		return -1;
	} //fin del if((map
	log_info(log, SNOMBREPROCESO, "Message info: %s",
			"Se logro mapear el dispositivo");
	return 0;
} //fin del fMapear
/******************************************************************************/
/* Nombre de Funcion: fLeerSector                                             */
/* Parametros: iSector:Secotr que se va a leer                                */
/*             info: informacion donde se guardara el contenido del sector    */
/*               al sector qeu se va a leer                                   */
/* Descripcion: Procedure que lee un sector                                   */
/******************************************************************************/

int fLeerSector(stNodoPedido *stAux, int iPista,int iTiempoLectura) {
	/*declaracion de variables locales*/

	int iCuenta;
	/*fin de la declaracion de variables locales*/

	//Exito, y operaciones a realizar

	iPosicionCabezal=(iPista*iSectores)+stAux->iNumSector;
	iCuenta = iPosicionCabezal * 512;
	memcpy(stAux->info, map + iCuenta, sizeof(char)*512);
	sleep(iTiempoLectura);
	if(posix_madvise(map, estadobuf.st_size, POSIX_MADV_WILLNEED) != 0){
	  return -1;
	}
    iPosicionCabezal++;
	return 0;
} //fin del fLeerSector

/******************************************************************************/
/* Nombre de Funcion: fEscribirSector                                         */
/* Parametros: stAux:Estructura formada por el sector y la info que voy       */
/*                   escribir                                                 */
/*             iTiempoEscritura: Tiempo de escritura                          */
/* Descripcion: Procedure que escribe un sector                               */
/******************************************************************************/

int fEscribirSector(stNodoPedido *stAux, int iPista,int iTiempoEscritura) {
	/*declaracion de variables locales*/

	int iCuenta;
	/*fin de la declaracion de variables locales*/

	/*Sacamos la cuenta a partir de que sector tenemos que escribir*/
	iPosicionCabezal=(iPista*iSectores)+stAux->iNumSector;
	iCuenta = iPosicionCabezal * 512;
	memcpy(map + iCuenta, stAux->info,sizeof(char)*512);
	sleep(iTiempoEscritura);
	if (msync(map, estadobuf.st_size, MS_ASYNC) == -1) {
		return -1;
	} //fin del if
	iPosicionCabezal++;

	return 0;
} //fin del fEscribirSector

/******************************************************************************/
/* Nombre de Funcion: pDesmapear                                              */
/* Parametros: sin parametros                                                 */
/* Descripcion: Procedure que desmapea el sector                              */
/******************************************************************************/

void pDesmapear(void) {
	close(descriptor);
	munmap(map, estadobuf.st_size);
} //fin del pDesmapear
